1   /*
2    * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  
27  /**
28   * Ported from awt_wm.c, SCCS v1.11, author Valeriy Ushakov
29   * Author: Denis Mikhalkin
30   */
31  package sun.awt.X11;
32  
33  import sun.misc.Unsafe;
34  import java.awt.Insets;
35  import java.awt.Frame;
36  import java.awt.Rectangle;
37  import java.util.Collection;
38  import java.util.HashMap;
39  import java.util.LinkedList;
40  import java.util.regex.Matcher;
41  import java.util.regex.Pattern;
42  import sun.util.logging.PlatformLogger;
43  
44  
45  /**
46   * Class incapsulating knowledge about window managers in general
47   * Descendants should provide some information about specific window manager.
48   */
49  final class XWM
50  {
51  
52      private final static PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XWM");
53      private final static PlatformLogger insLog = PlatformLogger.getLogger("sun.awt.X11.insets.XWM");
54      private final static PlatformLogger stateLog = PlatformLogger.getLogger("sun.awt.X11.states.XWM");
55  
56      static final XAtom XA_MWM_HINTS = new XAtom();
57  
58      private static Unsafe unsafe = XlibWrapper.unsafe;
59  
60  
61  /* Good old ICCCM */
62      static XAtom XA_WM_STATE = new XAtom();
63  
64  
65      XAtom XA_UTF8_STRING = XAtom.get("UTF8_STRING");    /* like STRING but encoding is UTF-8 */
66  
67  /* Currently we only care about max_v and max_h in _NET_WM_STATE */
68      final static int AWT_NET_N_KNOWN_STATES=2;
69  
70  /* Enlightenment */
71      final static XAtom XA_E_FRAME_SIZE = new XAtom();
72  
73  /* KWin (KDE2) */
74      final static XAtom XA_KDE_NET_WM_FRAME_STRUT = new XAtom();
75  
76  /* KWM (KDE 1.x) OBSOLETE??? */
77      final static XAtom XA_KWM_WIN_ICONIFIED = new XAtom();
78      final static XAtom XA_KWM_WIN_MAXIMIZED = new XAtom();
79  
80  /* OpenLook */
81      final static XAtom XA_OL_DECOR_DEL = new XAtom();
82      final static XAtom XA_OL_DECOR_HEADER = new XAtom();
83      final static XAtom XA_OL_DECOR_RESIZE = new XAtom();
84      final static XAtom XA_OL_DECOR_PIN = new XAtom();
85      final static XAtom XA_OL_DECOR_CLOSE = new XAtom();
86  
87  /* EWMH */
88      final static XAtom XA_NET_FRAME_EXTENTS = new XAtom();
89      final static XAtom XA_NET_REQUEST_FRAME_EXTENTS = new XAtom();
90  
91      final static int
92          UNDETERMINED_WM = 1,
93          NO_WM = 2,
94          OTHER_WM = 3,
95          OPENLOOK_WM = 4,
96          MOTIF_WM = 5,
97          CDE_WM = 6,
98          ENLIGHTEN_WM = 7,
99          KDE2_WM = 8,
100         SAWFISH_WM = 9,
101         ICE_WM = 10,
102         METACITY_WM = 11,
103         COMPIZ_WM = 12,
104         LG3D_WM = 13;
105     public String toString() {
106         switch  (WMID) {
107           case NO_WM:
108               return "NO WM";
109           case OTHER_WM:
110               return "Other WM";
111           case OPENLOOK_WM:
112               return "OPENLOOK";
113           case MOTIF_WM:
114               return "MWM";
115           case CDE_WM:
116               return "DTWM";
117           case ENLIGHTEN_WM:
118               return "Enlightenment";
119           case KDE2_WM:
120               return "KWM2";
121           case SAWFISH_WM:
122               return "Sawfish";
123           case ICE_WM:
124               return "IceWM";
125           case METACITY_WM:
126               return "Metacity";
127           case COMPIZ_WM:
128               return "Compiz";
129           case LG3D_WM:
130               return "LookingGlass";
131           case UNDETERMINED_WM:
132           default:
133               return "Undetermined WM";
134         }
135     }
136 
137 
138     int WMID;
139     static final Insets zeroInsets = new Insets(0, 0, 0, 0);
140     static final Insets defaultInsets = new Insets(25, 5, 5, 5);
141 
142     XWM(int WMID) {
143         this.WMID = WMID;
144         initializeProtocols();
145         if (log.isLoggable(PlatformLogger.FINE)) log.fine("Window manager: " + toString());
146     }
147     int getID() {
148         return WMID;
149     }
150 
151 
152     static Insets normalize(Insets insets) {
153         if (insets.top > 64 || insets.top < 0) {
154             insets.top = 28;
155         }
156         if (insets.left > 32 || insets.left < 0) {
157             insets.left = 6;
158         }
159         if (insets.right > 32 || insets.right < 0) {
160             insets.right = 6;
161         }
162         if (insets.bottom > 32 || insets.bottom < 0) {
163             insets.bottom = 6;
164         }
165         return insets;
166     }
167 
168     static XNETProtocol g_net_protocol = null;
169     static XWINProtocol g_win_protocol = null;
170     static boolean isNetWMName(String name) {
171         if (g_net_protocol != null) {
172             return g_net_protocol.isWMName(name);
173         } else {
174             return false;
175         }
176     }
177 
178     static void initAtoms() {
179         final Object[][] atomInitList ={
180             { XA_WM_STATE,                      "WM_STATE"                  },
181 
182             { XA_KDE_NET_WM_FRAME_STRUT,    "_KDE_NET_WM_FRAME_STRUT"       },
183 
184             { XA_E_FRAME_SIZE,              "_E_FRAME_SIZE"                 },
185 
186             { XA_KWM_WIN_ICONIFIED,          "KWM_WIN_ICONIFIED"             },
187             { XA_KWM_WIN_MAXIMIZED,          "KWM_WIN_MAXIMIZED"             },
188 
189             { XA_OL_DECOR_DEL,               "_OL_DECOR_DEL"                 },
190             { XA_OL_DECOR_HEADER,            "_OL_DECOR_HEADER"              },
191             { XA_OL_DECOR_RESIZE,            "_OL_DECOR_RESIZE"              },
192             { XA_OL_DECOR_PIN,               "_OL_DECOR_PIN"                 },
193             { XA_OL_DECOR_CLOSE,             "_OL_DECOR_CLOSE"               },
194             { XA_MWM_HINTS,                  "_MOTIF_WM_HINTS"               },
195             { XA_NET_FRAME_EXTENTS,          "_NET_FRAME_EXTENTS"            },
196             { XA_NET_REQUEST_FRAME_EXTENTS,  "_NET_REQUEST_FRAME_EXTENTS"    },
197         };
198 
199         String[] names = new String[atomInitList.length];
200         for (int index = 0; index < names.length; index++) {
201             names[index] = (String)atomInitList[index][1];
202         }
203 
204         int atomSize = XAtom.getAtomSize();
205         long atoms = unsafe.allocateMemory(names.length*atomSize);
206         XToolkit.awtLock();
207         try {
208             int status = XlibWrapper.XInternAtoms(XToolkit.getDisplay(), names, false, atoms);
209             if (status == 0) {
210                 return;
211             }
212             for (int atom = 0, atomPtr = 0; atom < names.length; atom++, atomPtr += atomSize) {
213                 ((XAtom)(atomInitList[atom][0])).setValues(XToolkit.getDisplay(), names[atom], XAtom.getAtom(atoms + atomPtr));
214             }
215         } finally {
216             XToolkit.awtUnlock();
217             unsafe.freeMemory(atoms);
218         }
219     }
220 
221     /*
222      * MUST BE CALLED UNDER AWTLOCK.
223      *
224      * If *any* window manager is running?
225      *
226      * According to ICCCM 2.0 section 4.3.
227      * WM will acquire ownership of a selection named WM_Sn, where n is
228      * the screen number.
229      *
230      * No selection owner, but, perhaps it is not ICCCM compliant WM
231      * (e.g. CDE/Sawfish).
232      * Try selecting for SubstructureRedirect, that only one client
233      * can select for, and if the request fails, than some other WM is
234      * already running.
235      *
236      * We also treat eXcursion as NO_WM.
237      */
238     private static boolean isNoWM() {
239         /*
240          * Quick checks for specific servers.
241          */
242         String vendor_string = XlibWrapper.ServerVendor(XToolkit.getDisplay());
243         if (vendor_string.indexOf("eXcursion") != -1) {
244             /*
245              * Use NO_WM since in all other aspects eXcursion is like not
246              * having a window manager running. I.e. it does not reparent
247              * top level shells.
248              */
249             if (insLog.isLoggable(PlatformLogger.FINE)) {
250                 insLog.finer("eXcursion means NO_WM");
251             }
252             return true;
253         }
254 
255         XSetWindowAttributes substruct = new XSetWindowAttributes();
256         try {
257             /*
258              * Let's check an owner of WM_Sn selection for the default screen.
259              */
260             final long default_screen_number =
261                 XlibWrapper.DefaultScreen(XToolkit.getDisplay());
262             final String selection_name = "WM_S" + default_screen_number;
263 
264             long selection_owner =
265                 XlibWrapper.XGetSelectionOwner(XToolkit.getDisplay(),
266                                                XAtom.get(selection_name).getAtom());
267             if (insLog.isLoggable(PlatformLogger.FINE)) {
268                 insLog.finer("selection owner of " + selection_name
269                              + " is " + selection_owner);
270             }
271 
272             if (selection_owner != XConstants.None) {
273                 return false;
274             }
275 
276             winmgr_running = false;
277             substruct.set_event_mask(XConstants.SubstructureRedirectMask);
278 
279             XToolkit.WITH_XERROR_HANDLER(detectWMHandler);
280             XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
281                                                 XToolkit.getDefaultRootWindow(),
282                                                 XConstants.CWEventMask,
283                                                 substruct.pData);
284             XToolkit.RESTORE_XERROR_HANDLER();
285 
286             /*
287              * If no WM is running then our selection for SubstructureRedirect
288              * succeeded and needs to be undone (hey we are *not* a WM ;-).
289              */
290             if (!winmgr_running) {
291                 substruct.set_event_mask(0);
292                 XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
293                                                     XToolkit.getDefaultRootWindow(),
294                                                     XConstants.CWEventMask,
295                                                     substruct.pData);
296                 if (insLog.isLoggable(PlatformLogger.FINE)) {
297                     insLog.finer("It looks like there is no WM thus NO_WM");
298                 }
299             }
300 
301             return !winmgr_running;
302         } finally {
303             substruct.dispose();
304         }
305     }
306 
307     static XAtom XA_ENLIGHTENMENT_COMMS = new XAtom("ENLIGHTENMENT_COMMS", false);
308     /*
309      * Helper function for isEnlightenment().
310      * Enlightenment uses STRING property for its comms window id.  Gaaa!
311      * The property is ENLIGHTENMENT_COMMS, STRING/8 and the string format
312      * is "WINID %8x".  Gee, I haven't been using scanf for *ages*... :-)
313      */
314     static long getECommsWindowIDProperty(long window) {
315 
316         if (!XA_ENLIGHTENMENT_COMMS.isInterned()) {
317             return 0;
318         }
319 
320         WindowPropertyGetter getter =
321             new WindowPropertyGetter(window, XA_ENLIGHTENMENT_COMMS, 0, 14, false,
322                                      XAtom.XA_STRING);
323         try {
324             int status = getter.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
325             if (status != XConstants.Success || getter.getData() == 0) {
326                 return 0;
327             }
328 
329             if (getter.getActualType() != XAtom.XA_STRING
330                 || getter.getActualFormat() != 8
331                 || getter.getNumberOfItems() != 14 || getter.getBytesAfter() != 0)
332             {
333                 return 0;
334             }
335 
336             // Convert data to String, ASCII
337             byte[] bytes = XlibWrapper.getStringBytes(getter.getData());
338             String id = new String(bytes);
339 
340             log.finer("ENLIGHTENMENT_COMMS is " + id);
341 
342             // Parse WINID
343             Pattern winIdPat = Pattern.compile("WINID\\s+(\\p{XDigit}{0,8})");
344             try {
345                 Matcher match = winIdPat.matcher(id);
346                 if (match.matches()) {
347                     log.finest("Match group count: " + match.groupCount());
348                     String longId = match.group(1);
349                     log.finest("Match group 1 " + longId);
350                     long winid = Long.parseLong(longId, 16);
351                     log.finer("Enlightenment communication window " + winid);
352                     return winid;
353                 } else {
354                     log.finer("ENLIGHTENMENT_COMMS has wrong format");
355                     return 0;
356                 }
357             } catch (Exception e) {
358                 if (log.isLoggable(PlatformLogger.FINER)) {
359                     e.printStackTrace();
360                 }
361                 return 0;
362             }
363         } finally {
364             getter.dispose();
365         }
366     }
367 
368     /*
369      * Is Enlightenment WM running?  Congruent to awt_wm_checkAnchor, but
370      * uses STRING property peculiar to Enlightenment.
371      */
372     static boolean isEnlightenment() {
373 
374         long root_xref = getECommsWindowIDProperty(XToolkit.getDefaultRootWindow());
375         if (root_xref == 0) {
376             return false;
377         }
378 
379         long self_xref = getECommsWindowIDProperty(root_xref);
380         if (self_xref != root_xref) {
381             return false;
382         }
383 
384         return true;
385     }
386 
387     /*
388      * Is CDE running?
389      *
390      * XXX: This is hairy...  CDE is MWM as well.  It seems we simply test
391      * for default setup and will be bitten if user changes things...
392      *
393      * Check for _DT_SM_WINDOW_INFO(_DT_SM_WINDOW_INFO) on root.  Take the
394      * second element of the property and check for presence of
395      * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
396      *
397      * XXX: Any header that defines this structures???
398      */
399     static final XAtom XA_DT_SM_WINDOW_INFO = new XAtom("_DT_SM_WINDOW_INFO", false);
400     static final XAtom XA_DT_SM_STATE_INFO = new XAtom("_DT_SM_STATE_INFO", false);
401     static boolean isCDE() {
402 
403         if (!XA_DT_SM_WINDOW_INFO.isInterned()) {
404             log.finer("{0} is not interned", XA_DT_SM_WINDOW_INFO);
405             return false;
406         }
407 
408         WindowPropertyGetter getter =
409             new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
410                                      XA_DT_SM_WINDOW_INFO, 0, 2,
411                                      false, XA_DT_SM_WINDOW_INFO);
412         try {
413             int status = getter.execute();
414             if (status != XConstants.Success || getter.getData() == 0) {
415                 log.finer("Getting of _DT_SM_WINDOW_INFO is not successfull");
416                 return false;
417             }
418             if (getter.getActualType() != XA_DT_SM_WINDOW_INFO.getAtom()
419                 || getter.getActualFormat() != 32
420                 || getter.getNumberOfItems() != 2 || getter.getBytesAfter() != 0)
421             {
422                 log.finer("Wrong format of _DT_SM_WINDOW_INFO");
423                 return false;
424             }
425 
426             long wmwin = Native.getWindow(getter.getData(), 1); //unsafe.getInt(getter.getData()+4);
427 
428             if (wmwin == 0) {
429                 log.fine("WARNING: DT_SM_WINDOW_INFO exists but returns zero windows");
430                 return false;
431             }
432 
433             /* Now check that this window has _DT_SM_STATE_INFO (ignore contents) */
434             if (!XA_DT_SM_STATE_INFO.isInterned()) {
435                 log.finer("{0} is not interned", XA_DT_SM_STATE_INFO);
436                 return false;
437             }
438             WindowPropertyGetter getter2 =
439                 new WindowPropertyGetter(wmwin, XA_DT_SM_STATE_INFO, 0, 1,
440                                          false, XA_DT_SM_STATE_INFO);
441             try {
442                 status = getter2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
443 
444 
445                 if (status != XConstants.Success || getter2.getData() == 0) {
446                     log.finer("Getting of _DT_SM_STATE_INFO is not successfull");
447                     return false;
448                 }
449                 if (getter2.getActualType() != XA_DT_SM_STATE_INFO.getAtom()
450                     || getter2.getActualFormat() != 32)
451                 {
452                     log.finer("Wrong format of _DT_SM_STATE_INFO");
453                     return false;
454                 }
455 
456                 return true;
457             } finally {
458                 getter2.dispose();
459             }
460         } finally {
461             getter.dispose();
462         }
463     }
464 
465     /*
466      * Is MWM running?  (Note that CDE will test positive as well).
467      *
468      * Check for _MOTIF_WM_INFO(_MOTIF_WM_INFO) on root.  Take the
469      * second element of the property and check for presence of
470      * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
471      */
472     static final XAtom XA_MOTIF_WM_INFO = new XAtom("_MOTIF_WM_INFO", false);
473     static final XAtom XA_DT_WORKSPACE_CURRENT = new XAtom("_DT_WORKSPACE_CURRENT", false);
474     static boolean isMotif() {
475 
476         if (!(XA_MOTIF_WM_INFO.isInterned()/* && XA_DT_WORKSPACE_CURRENT.isInterned()*/) ) {
477             return false;
478         }
479 
480         WindowPropertyGetter getter =
481             new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
482                                      XA_MOTIF_WM_INFO, 0,
483                                      MWMConstants.PROP_MOTIF_WM_INFO_ELEMENTS,
484                                      false, XA_MOTIF_WM_INFO);
485         try {
486             int status = getter.execute();
487 
488             if (status != XConstants.Success || getter.getData() == 0) {
489                 return false;
490             }
491 
492             if (getter.getActualType() != XA_MOTIF_WM_INFO.getAtom()
493                 || getter.getActualFormat() != 32
494                 || getter.getNumberOfItems() != MWMConstants.PROP_MOTIF_WM_INFO_ELEMENTS
495                 || getter.getBytesAfter() != 0)
496             {
497                 return false;
498             }
499 
500             long wmwin = Native.getLong(getter.getData(), 1);
501             if (wmwin != 0) {
502                 if (XA_DT_WORKSPACE_CURRENT.isInterned()) {
503                     /* Now check that this window has _DT_WORKSPACE_CURRENT */
504                     XAtom[] curws = XA_DT_WORKSPACE_CURRENT.getAtomListProperty(wmwin);
505                     if (curws.length == 0) {
506                         return false;
507                     }
508                     return true;
509                 } else {
510                     // No DT_WORKSPACE, however in our tests MWM sometimes can be without desktop -
511                     // and that is still MWM.  So simply check for the validity of this window
512                     // (through WM_STATE property).
513                     WindowPropertyGetter state_getter =
514                         new WindowPropertyGetter(wmwin,
515                                                  XA_WM_STATE,
516                                                  0, 1, false,
517                                                  XA_WM_STATE);
518                     try {
519                         if (state_getter.execute() == XConstants.Success &&
520                             state_getter.getData() != 0 &&
521                             state_getter.getActualType() == XA_WM_STATE.getAtom())
522                         {
523                             return true;
524                         }
525                     } finally {
526                         state_getter.dispose();
527                     }
528                 }
529             }
530         } finally {
531             getter.dispose();
532         }
533         return false;
534     }
535 
536     /*
537      * Is Sawfish running?
538      */
539     static boolean isSawfish() {
540         return isNetWMName("Sawfish");
541     }
542 
543     /*
544      * Is KDE2 (KWin) running?
545      */
546     static boolean isKDE2() {
547         return isNetWMName("KWin");
548     }
549 
550     static boolean isCompiz() {
551         return isNetWMName("compiz");
552     }
553 
554     static boolean isLookingGlass() {
555         return isNetWMName("LG3D");
556     }
557 
558     /*
559      * Is Metacity running?
560      */
561     static boolean isMetacity() {
562         return isNetWMName("Metacity");
563 //         || (
564 //             XA_NET_SUPPORTING_WM_CHECK.
565 //             getIntProperty(XToolkit.getDefaultRootWindow(), XA_NET_SUPPORTING_WM_CHECK.
566 //                            getIntProperty(XToolkit.getDefaultRootWindow(), XAtom.XA_CARDINAL)) == 0);
567     }
568 
569     static boolean isNonReparentingWM() {
570         return (XWM.getWMID() == XWM.COMPIZ_WM || XWM.getWMID() == XWM.LG3D_WM);
571     }
572 
573     /*
574      * Prepare IceWM check.
575      *
576      * The only way to detect IceWM, seems to be by setting
577      * _ICEWM_WINOPTHINT(_ICEWM_WINOPTHINT/8) on root and checking if it
578      * was immediately deleted by IceWM.
579      *
580      * But messing with PropertyNotify here is way too much trouble, so
581      * approximate the check by setting the property in this function and
582      * checking if it still exists later on.
583      *
584      * Gaa, dirty dances...
585      */
586     static final XAtom XA_ICEWM_WINOPTHINT = new XAtom("_ICEWM_WINOPTHINT", false);
587     static final char opt[] = {
588         'A','W','T','_','I','C','E','W','M','_','T','E','S','T','\0',
589         'a','l','l','W','o','r','k','s','p','a','c','e','s','\0',
590         '0','\0'
591     };
592     static boolean prepareIsIceWM() {
593         /*
594          * Choose something innocuous: "AWT_ICEWM_TEST allWorkspaces 0".
595          * IceWM expects "class\0option\0arg\0" with zero bytes as delimiters.
596          */
597 
598         if (!XA_ICEWM_WINOPTHINT.isInterned()) {
599             log.finer("{0} is not interned", XA_ICEWM_WINOPTHINT);
600             return false;
601         }
602 
603         XToolkit.awtLock();
604         try {
605             XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
606             XlibWrapper.XChangePropertyS(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
607                                          XA_ICEWM_WINOPTHINT.getAtom(),
608                                          XA_ICEWM_WINOPTHINT.getAtom(),
609                                          8, XConstants.PropModeReplace,
610                                          new String(opt));
611             XToolkit.RESTORE_XERROR_HANDLER();
612 
613             if (XToolkit.saved_error != null && XToolkit.saved_error.get_error_code() != XConstants.Success) {
614                 log.finer("Erorr getting XA_ICEWM_WINOPTHINT property");
615                 return false;
616             }
617             log.finer("Prepared for IceWM detection");
618             return true;
619         } finally {
620             XToolkit.awtUnlock();
621         }
622     }
623 
624     /*
625      * Is IceWM running?
626      *
627      * Note well: Only call this if awt_wm_prepareIsIceWM succeeded, or a
628      * false positive will be reported.
629      */
630     static boolean isIceWM() {
631         if (!XA_ICEWM_WINOPTHINT.isInterned()) {
632             log.finer("{0} is not interned", XA_ICEWM_WINOPTHINT);
633             return false;
634         }
635 
636         WindowPropertyGetter getter =
637             new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
638                                      XA_ICEWM_WINOPTHINT, 0, 0xFFFF,
639                                      true, XA_ICEWM_WINOPTHINT);
640         try {
641             int status = getter.execute();
642             boolean res = (status == XConstants.Success && getter.getActualType() != 0);
643             log.finer("Status getting XA_ICEWM_WINOPTHINT: " + !res);
644             return !res || isNetWMName("IceWM");
645         } finally {
646             getter.dispose();
647         }
648     }
649 
650     /*
651      * Is OpenLook WM running?
652      *
653      * This one is pretty lame, but the only property peculiar to OLWM is
654      * _SUN_WM_PROTOCOLS(ATOM[]).  Fortunately, olwm deletes it on exit.
655      */
656     static final XAtom XA_SUN_WM_PROTOCOLS = new XAtom("_SUN_WM_PROTOCOLS", false);
657     static boolean isOpenLook() {
658         if (!XA_SUN_WM_PROTOCOLS.isInterned()) {
659             return false;
660         }
661 
662         XAtom[] list = XA_SUN_WM_PROTOCOLS.getAtomListProperty(XToolkit.getDefaultRootWindow());
663         return (list.length != 0);
664     }
665 
666     /*
667      * Temporary error handler that checks if selecting for
668      * SubstructureRedirect failed.
669      */
670     private static boolean winmgr_running = false;
671     private static XErrorHandler detectWMHandler = new XErrorHandler.XBaseErrorHandler() {
672         @Override
673         public int handleError(long display, XErrorEvent err) {
674             if ((err.get_request_code() == XProtocolConstants.X_ChangeWindowAttributes) &&
675                 (err.get_error_code() == XConstants.BadAccess))
676             {
677                 winmgr_running = true;
678                 return 0;
679             }
680             return super.handleError(display, err);
681         }
682     };
683 
684     /*
685      * Make an educated guess about running window manager.
686      * XXX: ideally, we should detect wm restart.
687      */
688     static int awt_wmgr = XWM.UNDETERMINED_WM;
689     static XWM wm;
690     static XWM getWM() {
691         if (wm == null) {
692             wm = new XWM(awt_wmgr = getWMID()/*XWM.OTHER_WM*/);
693         }
694         return wm;
695     }
696     static int getWMID() {
697         if (insLog.isLoggable(PlatformLogger.FINEST)) {
698             insLog.finest("awt_wmgr = " + awt_wmgr);
699         }
700         /*
701          * Ideally, we should support cases when a different WM is started
702          * during a Java app lifetime.
703          */
704 
705         if (awt_wmgr != XWM.UNDETERMINED_WM) {
706             return awt_wmgr;
707         }
708 
709         XSetWindowAttributes substruct = new XSetWindowAttributes();
710         XToolkit.awtLock();
711         try {
712             if (isNoWM()) {
713                 awt_wmgr = XWM.NO_WM;
714                 return awt_wmgr;
715             }
716 
717             // Initialize _NET protocol - used to detect Window Manager.
718             // Later, WM will initialize its own version of protocol
719             XNETProtocol l_net_protocol = g_net_protocol = new XNETProtocol();
720             l_net_protocol.detect();
721             if (log.isLoggable(PlatformLogger.FINE) && l_net_protocol.active()) {
722                 log.fine("_NET_WM_NAME is " + l_net_protocol.getWMName());
723             }
724             XWINProtocol win = g_win_protocol = new XWINProtocol();
725             win.detect();
726 
727             /* actual check for IceWM to follow below */
728             boolean doIsIceWM = prepareIsIceWM(); /* and let IceWM to act */
729 
730             /*
731              * Ok, some WM is out there.  Check which one by testing for
732              * "distinguishing" atoms.
733              */
734             if (isEnlightenment()) {
735                 awt_wmgr = XWM.ENLIGHTEN_WM;
736             } else if (isMetacity()) {
737                 awt_wmgr = XWM.METACITY_WM;
738             } else if (isSawfish()) {
739                 awt_wmgr = XWM.SAWFISH_WM;
740             } else if (isKDE2()) {
741                 awt_wmgr =XWM.KDE2_WM;
742             } else if (isCompiz()) {
743                 awt_wmgr = XWM.COMPIZ_WM;
744             } else if (isLookingGlass()) {
745                 awt_wmgr = LG3D_WM;
746             } else if (doIsIceWM && isIceWM()) {
747                 awt_wmgr = XWM.ICE_WM;
748             }
749             /*
750              * We don't check for legacy WM when we already know that WM
751              * supports WIN or _NET wm spec.
752              */
753             else if (l_net_protocol.active()) {
754                 awt_wmgr = XWM.OTHER_WM;
755             } else if (win.active()) {
756                 awt_wmgr = XWM.OTHER_WM;
757             }
758             /*
759              * Check for legacy WMs.
760              */
761             else if (isCDE()) { /* XXX: must come before isMotif */
762                 awt_wmgr = XWM.CDE_WM;
763             } else if (isMotif()) {
764                 awt_wmgr = XWM.MOTIF_WM;
765             } else if (isOpenLook()) {
766                 awt_wmgr = XWM.OPENLOOK_WM;
767             } else {
768                 awt_wmgr = XWM.OTHER_WM;
769             }
770 
771             return awt_wmgr;
772         } finally {
773             XToolkit.awtUnlock();
774             substruct.dispose();
775         }
776     }
777 
778 
779 /*****************************************************************************\
780  *
781  * Size and decoration hints ...
782  *
783 \*****************************************************************************/
784 
785 
786     /*
787      * Remove size hints specified by the mask.
788      * XXX: Why do we need this in the first place???
789      */
790     static void removeSizeHints(XDecoratedPeer window, long mask) {
791         mask &= XUtilConstants.PMaxSize | XUtilConstants.PMinSize;
792 
793         XToolkit.awtLock();
794         try {
795             XSizeHints hints = window.getHints();
796             if ((hints.get_flags() & mask) == 0) {
797                 return;
798             }
799 
800             hints.set_flags(hints.get_flags() & ~mask);
801             if (insLog.isLoggable(PlatformLogger.FINER)) insLog.finer("Setting hints, flags " + XlibWrapper.hintsToString(hints.get_flags()));
802             XlibWrapper.XSetWMNormalHints(XToolkit.getDisplay(),
803                                           window.getWindow(),
804                                           hints.pData);
805         } finally {
806             XToolkit.awtUnlock();
807         }
808     }
809 
810     /*
811      * If MWM_DECOR_ALL bit is set, then the rest of the bit-mask is taken
812      * to be subtracted from the decorations.  Normalize decoration spec
813      * so that we can map motif decor to something else bit-by-bit in the
814      * rest of the code.
815      */
816     static int normalizeMotifDecor(int decorations) {
817         if ((decorations & MWMConstants.MWM_DECOR_ALL) == 0) {
818             return decorations;
819         }
820         int d = MWMConstants.MWM_DECOR_BORDER | MWMConstants.MWM_DECOR_RESIZEH
821             | MWMConstants.MWM_DECOR_TITLE
822             | MWMConstants.MWM_DECOR_MENU | MWMConstants.MWM_DECOR_MINIMIZE
823             | MWMConstants.MWM_DECOR_MAXIMIZE;
824         d &= ~decorations;
825         return d;
826     }
827 
828     /*
829      * If MWM_FUNC_ALL bit is set, then the rest of the bit-mask is taken
830      * to be subtracted from the functions.  Normalize function spec
831      * so that we can map motif func to something else bit-by-bit in the
832      * rest of the code.
833      */
834     static int normalizeMotifFunc(int functions) {
835         if ((functions & MWMConstants.MWM_FUNC_ALL) == 0) {
836             return functions;
837         }
838         int f = MWMConstants.MWM_FUNC_RESIZE |
839                 MWMConstants.MWM_FUNC_MOVE |
840                 MWMConstants.MWM_FUNC_MAXIMIZE |
841                 MWMConstants.MWM_FUNC_MINIMIZE |
842                 MWMConstants.MWM_FUNC_CLOSE;
843         f &= ~functions;
844         return f;
845     }
846 
847     /*
848      * Infer OL properties from MWM decorations.
849      * Use _OL_DECOR_DEL(ATOM[]) to remove unwanted ones.
850      */
851     static void setOLDecor(XWindow window, boolean resizable, int decorations) {
852         if (window == null) {
853             return;
854         }
855 
856         XAtomList decorDel = new XAtomList();
857         decorations = normalizeMotifDecor(decorations);
858         if (insLog.isLoggable(PlatformLogger.FINER)) insLog.finer("Setting OL_DECOR to " + Integer.toBinaryString(decorations));
859         if ((decorations & MWMConstants.MWM_DECOR_TITLE) == 0) {
860             decorDel.add(XA_OL_DECOR_HEADER);
861         }
862         if ((decorations & (MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE)) == 0) {
863             decorDel.add(XA_OL_DECOR_RESIZE);
864         }
865         if ((decorations & (MWMConstants.MWM_DECOR_MENU |
866                             MWMConstants.MWM_DECOR_MAXIMIZE |
867                             MWMConstants.MWM_DECOR_MINIMIZE)) == 0)
868         {
869             decorDel.add(XA_OL_DECOR_CLOSE);
870         }
871         if (decorDel.size() == 0) {
872             insLog.finer("Deleting OL_DECOR");
873             XA_OL_DECOR_DEL.DeleteProperty(window);
874         } else {
875             if (insLog.isLoggable(PlatformLogger.FINER)) insLog.finer("Setting OL_DECOR to " + decorDel);
876             XA_OL_DECOR_DEL.setAtomListProperty(window, decorDel);
877         }
878     }
879 
880     /*
881      * Set MWM decorations.  Set MWM functions depending on resizability.
882      */
883     static void setMotifDecor(XWindow window, boolean resizable, int decorations, int functions) {
884         /* Apparently some WMs don't implement MWM_*_ALL semantic correctly */
885         if ((decorations & MWMConstants.MWM_DECOR_ALL) != 0
886             && (decorations != MWMConstants.MWM_DECOR_ALL))
887         {
888             decorations = normalizeMotifDecor(decorations);
889         }
890         if ((functions & MWMConstants.MWM_FUNC_ALL) != 0
891             && (functions != MWMConstants.MWM_FUNC_ALL))
892         {
893             functions = normalizeMotifFunc(functions);
894         }
895 
896         PropMwmHints hints = window.getMWMHints();
897         hints.set_flags(hints.get_flags() |
898                         MWMConstants.MWM_HINTS_FUNCTIONS |
899                         MWMConstants.MWM_HINTS_DECORATIONS);
900         hints.set_functions(functions);
901         hints.set_decorations(decorations);
902 
903         if (stateLog.isLoggable(PlatformLogger.FINER)) stateLog.finer("Setting MWM_HINTS to " + hints);
904         window.setMWMHints(hints);
905     }
906 
907     /*
908      * Under some window managers if shell is already mapped, we MUST
909      * unmap and later remap in order to effect the changes we make in the
910      * window manager decorations.
911      *
912      * N.B.  This unmapping / remapping of the shell exposes a bug in
913      * X/Motif or the Motif Window Manager.  When you attempt to map a
914      * widget which is positioned (partially) off-screen, the window is
915      * relocated to be entirely on screen. Good idea.  But if both the x
916      * and the y coordinates are less than the origin (0,0), the first
917      * (re)map will move the window to the origin, and any subsequent
918      * (re)map will relocate the window at some other point on the screen.
919      * I have written a short Motif test program to discover this bug.
920      * This should occur infrequently and it does not cause any real
921      * problem.  So for now we'll let it be.
922      */
923     static boolean needRemap(XDecoratedPeer window) {
924         // Don't remap EmbeddedFrame,
925         // e.g. for TrayIcon it causes problems.
926         return !window.isEmbedded();
927     }
928 
929     /*
930      * Set decoration hints on the shell to wdata->decor adjusted
931      * appropriately if not resizable.
932      */
933     static void setShellDecor(XDecoratedPeer window) {
934         int decorations = window.getDecorations();
935         int functions = window.getFunctions();
936         boolean resizable = window.isResizable();
937 
938         if (!resizable) {
939             if ((decorations & MWMConstants.MWM_DECOR_ALL) != 0) {
940                 decorations |= MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE;
941             } else {
942                 decorations &= ~(MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE);
943             }
944         }
945         setMotifDecor(window, resizable, decorations, functions);
946         setOLDecor(window, resizable, decorations);
947 
948         /* Some WMs need remap to redecorate the window */
949         if (window.isShowing() && needRemap(window)) {
950             /*
951              * Do the re/mapping at the Xlib level.  Since we essentially
952              * work around a WM bug we don't want this hack to be exposed
953              * to Intrinsics (i.e. don't mess with grabs, callbacks etc).
954              */
955             window.xSetVisible(false);
956             XToolkit.XSync();
957             window.xSetVisible(true);
958         }
959     }
960 
961     /*
962      * Make specified shell resizable.
963      */
964     static void setShellResizable(XDecoratedPeer window) {
965         if (insLog.isLoggable(PlatformLogger.FINE)) insLog.fine("Setting shell resizable " + window);
966         XToolkit.awtLock();
967         try {
968             Rectangle shellBounds = window.getShellBounds();
969             shellBounds.translate(-window.currentInsets.left, -window.currentInsets.top);
970             window.updateSizeHints(window.getDimensions());
971             requestWMExtents(window.getWindow());
972             XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(),
973                                           shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height);
974             /* REMINDER: will need to revisit when setExtendedStateBounds is added */
975             //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced.
976             //We need to update frame's minimum size, not to reset it
977             removeSizeHints(window, XUtilConstants.PMaxSize);
978             window.updateMinimumSize();
979 
980             /* Restore decorations */
981             setShellDecor(window);
982         } finally {
983             XToolkit.awtUnlock();
984         }
985     }
986 
987     /*
988      * Make specified shell non-resizable.
989      * If justChangeSize is false, update decorations as well.
990      * @param shellBounds bounds of the shell window
991      */
992     static void setShellNotResizable(XDecoratedPeer window, WindowDimensions newDimensions, Rectangle shellBounds,
993                                      boolean justChangeSize)
994     {
995         if (insLog.isLoggable(PlatformLogger.FINE)) insLog.fine("Setting non-resizable shell " + window + ", dimensions " + newDimensions +
996                                                        ", shellBounds " + shellBounds +", just change size: " + justChangeSize);
997         XToolkit.awtLock();
998         try {
999             /* Fix min/max size hints at the specified values */
1000             if (!shellBounds.isEmpty()) {
1001                 window.updateSizeHints(newDimensions);
1002                 requestWMExtents(window.getWindow());
1003                 XToolkit.XSync();
1004                 XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(),
1005                                               shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height);
1006             }
1007             if (!justChangeSize) {  /* update decorations */
1008                 setShellDecor(window);
1009             }
1010         } finally {
1011             XToolkit.awtUnlock();
1012         }
1013     }
1014 
1015 /*****************************************************************************\
1016  * Protocols support
1017  */
1018     private HashMap<Class<?>, Collection<?>> protocolsMap = new HashMap<Class<?>, Collection<?>>();
1019     /**
1020      * Returns all protocols supporting given protocol interface
1021      */
1022     <T> Collection<T> getProtocols(Class<T> protocolInterface) {
1023         Collection<T> res = (Collection<T>) protocolsMap.get(protocolInterface);
1024         if (res != null) {
1025             return res;
1026         } else {
1027             return new LinkedList<T>();
1028         }
1029     }
1030 
1031     private <T> void addProtocol(Class<T> protocolInterface, T protocol) {
1032         Collection<T> protocols = getProtocols(protocolInterface);
1033         protocols.add(protocol);
1034         protocolsMap.put(protocolInterface, protocols);
1035     }
1036 
1037     boolean supportsDynamicLayout() {
1038         int wm = getWMID();
1039         switch (wm) {
1040           case XWM.ENLIGHTEN_WM:
1041           case XWM.KDE2_WM:
1042           case XWM.SAWFISH_WM:
1043           case XWM.ICE_WM:
1044           case XWM.METACITY_WM:
1045               return true;
1046           case XWM.OPENLOOK_WM:
1047           case XWM.MOTIF_WM:
1048           case XWM.CDE_WM:
1049               return false;
1050           default:
1051               return false;
1052         }
1053     }
1054 
1055 
1056     /**
1057      * Check if state is supported.
1058      * Note that a compound state is always reported as not supported.
1059      * Note also that MAXIMIZED_BOTH is considered not a compound state.
1060      * Therefore, a compound state is just ICONIFIED | anything else.
1061      *
1062      */
1063     boolean supportsExtendedState(int state) {
1064         switch (state) {
1065           case Frame.MAXIMIZED_VERT:
1066           case Frame.MAXIMIZED_HORIZ:
1067               /*
1068                * WMs that talk NET/WIN protocol, but do not support
1069                * unidirectional maximization.
1070                */
1071               if (getWMID() == METACITY_WM) {
1072                   /* "This is a deliberate policy decision." -hp */
1073                   return false;
1074               }
1075               /* FALLTROUGH */
1076           case Frame.MAXIMIZED_BOTH:
1077               for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1078                   if (proto.supportsState(state)) {
1079                       return true;
1080                   }
1081               }
1082           default:
1083               return false;
1084         }
1085     }
1086 
1087 /*****************************************************************************\
1088  *
1089  * Reading state from different protocols
1090  *
1091 \*****************************************************************************/
1092 
1093 
1094     int getExtendedState(XWindowPeer window) {
1095         int state = 0;
1096         for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1097             state |= proto.getState(window);
1098         }
1099         if (state != 0) {
1100             return state;
1101         } else {
1102             return Frame.NORMAL;
1103         }
1104     }
1105 
1106 /*****************************************************************************\
1107  *
1108  * Notice window state change when WM changes a property on the window ...
1109  *
1110 \*****************************************************************************/
1111 
1112 
1113     /*
1114      * Check if property change is a window state protocol message.
1115      */
1116     boolean isStateChange(XDecoratedPeer window, XPropertyEvent e) {
1117         if (!window.isShowing()) {
1118             stateLog.finer("Window is not showing");
1119             return false;
1120         }
1121 
1122         int wm_state = window.getWMState();
1123         if (wm_state == XUtilConstants.WithdrawnState) {
1124             stateLog.finer("WithdrawnState");
1125             return false;
1126         } else {
1127             stateLog.finer("Window WM_STATE is " + wm_state);
1128         }
1129         boolean is_state_change = false;
1130         if (e.get_atom() == XA_WM_STATE.getAtom()) {
1131             is_state_change = true;
1132         }
1133 
1134         for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1135             is_state_change |= proto.isStateChange(e);
1136             stateLog.finest(proto + ": is state changed = " + is_state_change);
1137         }
1138         return is_state_change;
1139     }
1140 
1141     /*
1142      * Returns current state (including extended) of a given window.
1143      */
1144     int getState(XDecoratedPeer window) {
1145         int res = 0;
1146         final int wm_state = window.getWMState();
1147         if (wm_state == XUtilConstants.IconicState) {
1148             res = Frame.ICONIFIED;
1149         } else {
1150             res = Frame.NORMAL;
1151         }
1152         res |= getExtendedState(window);
1153         return res;
1154     }
1155 
1156 /*****************************************************************************\
1157  *
1158  * Setting/changing window state ...
1159  *
1160 \*****************************************************************************/
1161 
1162     /**
1163      * Moves window to the specified layer, layer is one of the constants defined
1164      * in XLayerProtocol
1165      */
1166     void setLayer(XWindowPeer window, int layer) {
1167         for (XLayerProtocol proto : getProtocols(XLayerProtocol.class)) {
1168             if (proto.supportsLayer(layer)) {
1169                 proto.setLayer(window, layer);
1170             }
1171         }
1172         XToolkit.XSync();
1173     }
1174 
1175     void setExtendedState(XWindowPeer window, int state) {
1176         for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1177             if (proto.supportsState(state)) {
1178                 proto.setState(window, state);
1179                 break;
1180             }
1181         }
1182 
1183         if (!window.isShowing()) {
1184             /*
1185              * Purge KWM bits.
1186              * Not really tested with KWM, only with WindowMaker.
1187              */
1188             XToolkit.awtLock();
1189             try {
1190                 XlibWrapper.XDeleteProperty(XToolkit.getDisplay(),
1191                                             window.getWindow(),
1192                                             XA_KWM_WIN_ICONIFIED.getAtom());
1193                 XlibWrapper.XDeleteProperty(XToolkit.getDisplay(),
1194                                             window.getWindow(),
1195                                             XA_KWM_WIN_MAXIMIZED.getAtom());
1196             }
1197             finally {
1198                 XToolkit.awtUnlock();
1199             }
1200         }
1201         XToolkit.XSync();
1202     }
1203 
1204 
1205     /*
1206      * Work around for 4775545.
1207      *
1208      * If WM exits while the top-level is shaded, the shaded hint remains
1209      * on the top-level properties.  When WM restarts and sees the shaded
1210      * window it can reparent it into a "pre-shaded" decoration frame
1211      * (Metacity does), and our insets logic will go crazy, b/c it will
1212      * see a huge nagative bottom inset.  There's no clean solution for
1213      * this, so let's just be weasels and drop the shaded hint if we
1214      * detect that WM exited.  NB: we are in for a race condition with WM
1215      * restart here.  NB2: e.g. WindowMaker saves the state in a private
1216      * property that this code knows nothing about, so this workaround is
1217      * not effective; other WMs might play similar tricks.
1218      */
1219     void unshadeKludge(XDecoratedPeer window) {
1220         assert(window.isShowing());
1221 
1222         for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1223             proto.unshadeKludge(window);
1224         }
1225         XToolkit.XSync();
1226     }
1227 
1228     static boolean inited = false;
1229     static void init() {
1230         if (inited) {
1231             return;
1232         }
1233 
1234         initAtoms();
1235         getWM();
1236         inited = true;
1237     }
1238 
1239     void initializeProtocols() {
1240         XNETProtocol net_protocol = g_net_protocol;
1241         if (net_protocol != null) {
1242             if (!net_protocol.active()) {
1243                 net_protocol = null;
1244             } else {
1245                 if (net_protocol.doStateProtocol()) {
1246                     addProtocol(XStateProtocol.class, net_protocol);
1247                 }
1248                 if (net_protocol.doLayerProtocol()) {
1249                     addProtocol(XLayerProtocol.class, net_protocol);
1250                 }
1251             }
1252         }
1253 
1254         XWINProtocol win = g_win_protocol;
1255         if (win != null) {
1256             if (win.active()) {
1257                 if (win.doStateProtocol()) {
1258                     addProtocol(XStateProtocol.class, win);
1259                 }
1260                 if (win.doLayerProtocol()) {
1261                     addProtocol(XLayerProtocol.class, win);
1262                 }
1263             }
1264         }
1265     }
1266 
1267     HashMap storedInsets = new HashMap();
1268     Insets guessInsets(XDecoratedPeer window) {
1269         Insets res = (Insets)storedInsets.get(window.getClass());
1270         if (res == null) {
1271             switch (WMID) {
1272               case ENLIGHTEN_WM:
1273                   res = new Insets(19, 4, 4, 4);
1274                   break;
1275               case CDE_WM:
1276                   res = new Insets(28, 6, 6, 6);
1277                   break;
1278               case NO_WM:
1279               case LG3D_WM:
1280                   res = zeroInsets;
1281                   break;
1282               case MOTIF_WM:
1283               case OPENLOOK_WM:
1284               default:
1285                   res = defaultInsets;
1286             }
1287         }
1288         if (insLog.isLoggable(PlatformLogger.FINEST)) insLog.finest("WM guessed insets: " + res);
1289         return res;
1290     }
1291     /*
1292      * Some buggy WMs ignore window gravity when processing
1293      * ConfigureRequest and position window as if the gravity is Static.
1294      * We work around this in MWindowPeer.pReshape().
1295      *
1296      * Starting with 1.5 we have introduced an Environment variable
1297      * _JAVA_AWT_WM_STATIC_GRAVITY that can be set to indicate to Java
1298      * explicitly that the WM has this behaviour, example is FVWM.
1299      */
1300 
1301     static int awtWMStaticGravity = -1;
1302     static boolean configureGravityBuggy() {
1303 
1304         if (awtWMStaticGravity == -1) {
1305             awtWMStaticGravity = (XToolkit.getEnv("_JAVA_AWT_WM_STATIC_GRAVITY") != null) ? 1 : 0;
1306         }
1307 
1308         if (awtWMStaticGravity == 1) {
1309             return true;
1310         }
1311 
1312         switch(getWMID()) {
1313           case XWM.ICE_WM:
1314               /*
1315                * See bug #228981 at IceWM's SourceForge pages.
1316                * Latest stable version 1.0.8-6 still has this problem.
1317                */
1318               /**
1319                * Version 1.2.2 doesn't have this problem
1320                */
1321               // Detect IceWM version
1322               if (g_net_protocol != null) {
1323                   String wm_name = g_net_protocol.getWMName();
1324                   Pattern pat = Pattern.compile("^IceWM (\\d+)\\.(\\d+)\\.(\\d+).*$");
1325                   try {
1326                       Matcher match = pat.matcher(wm_name);
1327                       if (match.matches()) {
1328                           int v1 = Integer.parseInt(match.group(1));
1329                           int v2 = Integer.parseInt(match.group(2));
1330                           int v3 = Integer.parseInt(match.group(3));
1331                           return !(v1 > 1 || (v1 == 1 && (v2 > 2 || (v2 == 2 && v3 >=2))));
1332                       }
1333                   } catch (Exception e) {
1334                       return true;
1335                   }
1336               }
1337               return true;
1338           case XWM.ENLIGHTEN_WM:
1339               /* At least E16 is buggy. */
1340               return true;
1341           default:
1342               return false;
1343         }
1344     }
1345 
1346     /*
1347      * @return if WM implements the insets property - returns insets with values
1348      * specified in that property, null otherwise.
1349      */
1350     public static Insets getInsetsFromExtents(long window) {
1351         if (window == XConstants.None) {
1352             return null;
1353         }
1354         XNETProtocol net_protocol = getWM().getNETProtocol();
1355         if (net_protocol != null && net_protocol.active()) {
1356             Insets insets = getInsetsFromProp(window, XA_NET_FRAME_EXTENTS);
1357             insLog.fine("_NET_FRAME_EXTENTS: {0}", insets);
1358 
1359             if (insets != null) {
1360                 return insets;
1361             }
1362         }
1363         switch(getWMID()) {
1364           case XWM.KDE2_WM:
1365               return getInsetsFromProp(window, XA_KDE_NET_WM_FRAME_STRUT);
1366           case XWM.ENLIGHTEN_WM:
1367               return getInsetsFromProp(window, XA_E_FRAME_SIZE);
1368           default:
1369               return null;
1370         }
1371     }
1372 
1373     /**
1374      * Helper function reads property of type CARDINAL[4] = { left, right, top, bottom }
1375      * and converts it to Insets object.
1376      */
1377     public static Insets getInsetsFromProp(long window, XAtom atom) {
1378         if (window == XConstants.None) {
1379             return null;
1380         }
1381 
1382         WindowPropertyGetter getter =
1383             new WindowPropertyGetter(window, atom,
1384                                      0, 4, false, XAtom.XA_CARDINAL);
1385         try {
1386             if (getter.execute() != XConstants.Success
1387                 || getter.getData() == 0
1388                 || getter.getActualType() != XAtom.XA_CARDINAL
1389                 || getter.getActualFormat() != 32)
1390             {
1391                 return null;
1392             } else {
1393                 return new Insets((int)Native.getCard32(getter.getData(), 2), // top
1394                                   (int)Native.getCard32(getter.getData(), 0), // left
1395                                   (int)Native.getCard32(getter.getData(), 3), // bottom
1396                                   (int)Native.getCard32(getter.getData(), 1)); // right
1397             }
1398         } finally {
1399             getter.dispose();
1400         }
1401     }
1402 
1403     /**
1404      * Asks WM to fill Frame Extents (insets) for the window.
1405      */
1406     public static void requestWMExtents(long window) {
1407         if (window == XConstants.None) { // not initialized
1408             return;
1409         }
1410 
1411         log.fine("Requesting FRAME_EXTENTS");
1412 
1413         XClientMessageEvent msg = new XClientMessageEvent();
1414         msg.zero();
1415         msg.set_type(XConstants.ClientMessage);
1416         msg.set_display(XToolkit.getDisplay());
1417         msg.set_window(window);
1418         msg.set_format(32);
1419         XToolkit.awtLock();
1420         try {
1421             XNETProtocol net_protocol = getWM().getNETProtocol();
1422             if (net_protocol != null && net_protocol.active()) {
1423                 msg.set_message_type(XA_NET_REQUEST_FRAME_EXTENTS.getAtom());
1424                 XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
1425                                        false,
1426                                        XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
1427                                        msg.getPData());
1428             }
1429             if (getWMID() == XWM.KDE2_WM) {
1430                 msg.set_message_type(XA_KDE_NET_WM_FRAME_STRUT.getAtom());
1431                 XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
1432                                        false,
1433                                        XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
1434                                        msg.getPData());
1435             }
1436             // XXX: should we wait for response? XIfEvent() would be useful here :)
1437         } finally {
1438             XToolkit.awtUnlock();
1439             msg.dispose();
1440         }
1441     }
1442 
1443     /* syncTopLEvelPos() is necessary to insure that the window manager has in
1444      * fact moved us to our final position relative to the reParented WM window.
1445      * We have noted a timing window which our shell has not been moved so we
1446      * screw up the insets thinking they are 0,0.  Wait (for a limited period of
1447      * time to let the WM hava a chance to move us.
1448      * @param window window ID of the shell, assuming it is the window
1449      * which will NOT have zero coordinates after the complete
1450      * reparenting
1451      */
1452     boolean syncTopLevelPos(long window, XWindowAttributes attrs) {
1453         int tries = 0;
1454         XToolkit.awtLock();
1455         try {
1456             do {
1457                 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), window, attrs.pData);
1458                 if (attrs.get_x() != 0 || attrs.get_y() != 0) {
1459                     return true;
1460                 }
1461                 tries++;
1462                 XToolkit.XSync();
1463             } while (tries < 50);
1464         }
1465         finally {
1466             XToolkit.awtUnlock();
1467         }
1468         return false;
1469     }
1470 
1471     Insets getInsets(XDecoratedPeer win, long window, long parent) {
1472         /*
1473          * Unfortunately the concept of "insets" borrowed to AWT
1474          * from Win32 is *absolutely*, *unbelievably* foreign to
1475          * X11.  Few WMs provide the size of frame decor
1476          * (i.e. insets) in a property they set on the client
1477          * window, so we check if we can get away with just
1478          * peeking at it.  [Future versions of wm-spec might add a
1479          * standardized hint for this].
1480          *
1481          * Otherwise we do some special casing.  Actually the
1482          * fallback code ("default" case) seems to cover most of
1483          * the existing WMs (modulo Reparent/Configure order
1484          * perhaps?).
1485          *
1486          * Fallback code tries to account for the two most common cases:
1487          *
1488          * . single reparenting
1489          *       parent window is the WM frame
1490          *       [twm, olwm, sawfish]
1491          *
1492          * . double reparenting
1493          *       parent is a lining exactly the size of the client
1494          *       grandpa is the WM frame
1495          *       [mwm, e!, kwin, fvwm2 ... ]
1496          */
1497         Insets correctWM = XWM.getInsetsFromExtents(window);
1498         insLog.finer("Got insets from property: {0}", correctWM);
1499 
1500         if (correctWM == null) {
1501             correctWM = new Insets(0,0,0,0);
1502 
1503             correctWM.top = -1;
1504             correctWM.left = -1;
1505 
1506             XWindowAttributes lwinAttr = new XWindowAttributes();
1507             XWindowAttributes pattr = new XWindowAttributes();
1508             try {
1509                 switch (XWM.getWMID()) {
1510                   /* should've been done in awt_wm_getInsetsFromProp */
1511                   case XWM.ENLIGHTEN_WM: {
1512                       /* enlightenment does double reparenting */
1513                       syncTopLevelPos(parent, lwinAttr);
1514                       correctWM.left = lwinAttr.get_x();
1515                       correctWM.top = lwinAttr.get_y();
1516                       /*
1517                        * Now get the actual dimensions of the parent window
1518                        * resolve the difference.  We can't rely on the left
1519                        * to be equal to right or bottom...  Enlightment
1520                        * breaks that assumption.
1521                        */
1522                       XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1523                                                        XlibUtil.getParentWindow(parent),
1524                                                        pattr.pData);
1525                       correctWM.right = pattr.get_width() -
1526                           (lwinAttr.get_width() + correctWM.left);
1527                       correctWM.bottom = pattr.get_height() -
1528                           (lwinAttr.get_height() + correctWM.top);
1529 
1530                       break;
1531                   }
1532                   case XWM.ICE_WM: // for 1.2.2.
1533                   case XWM.KDE2_WM: /* should've been done in getInsetsFromProp */
1534                   case XWM.CDE_WM:
1535                   case XWM.MOTIF_WM: {
1536                       /* these are double reparenting too */
1537                       if (syncTopLevelPos(parent, lwinAttr)) {
1538                           correctWM.top = lwinAttr.get_y();
1539                           correctWM.left = lwinAttr.get_x();
1540                           correctWM.right = correctWM.left;
1541                           correctWM.bottom = correctWM.left;
1542                       } else {
1543                           return null;
1544                       }
1545                       break;
1546                   }
1547                   case XWM.SAWFISH_WM:
1548                   case XWM.OPENLOOK_WM: {
1549                       /* single reparenting */
1550                       syncTopLevelPos(window, lwinAttr);
1551                       correctWM.top    = lwinAttr.get_y();
1552                       correctWM.left   = lwinAttr.get_x();
1553                       correctWM.right  = correctWM.left;
1554                       correctWM.bottom = correctWM.left;
1555                       break;
1556                   }
1557                   case XWM.OTHER_WM:
1558                   default: {                /* this is very similar to the E! case above */
1559                       insLog.finest("Getting correct insets for OTHER_WM/default, parent: {0}", parent);
1560                       syncTopLevelPos(parent, lwinAttr);
1561                       int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1562                                                                     window, lwinAttr.pData);
1563                       status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1564                                                                 parent, pattr.pData);
1565                       if (lwinAttr.get_root() == parent) {
1566                           insLog.finest("our parent is root so insets should be zero");
1567                           correctWM = new Insets(0, 0, 0, 0);
1568                           break;
1569                       }
1570 
1571                       /*
1572                        * Check for double-reparenting WM.
1573                        *
1574                        * If the parent is exactly the same size as the
1575                        * top-level assume taht it's the "lining" window and
1576                        * that the grandparent is the actual frame (NB: we
1577                        * have already handled undecorated windows).
1578                        *
1579                        * XXX: what about timing issues that syncTopLevelPos
1580                        * is supposed to work around?
1581                        */
1582                       if (lwinAttr.get_x() == 0 && lwinAttr.get_y() == 0
1583                           && lwinAttr.get_width()+2*lwinAttr.get_border_width() == pattr.get_width()
1584                           && lwinAttr.get_height()+2*lwinAttr.get_border_width() == pattr.get_height())
1585                       {
1586                           insLog.finest("Double reparenting detected, pattr({2})={0}, lwinAttr({3})={1}",
1587                                         lwinAttr, pattr, parent, window);
1588                           lwinAttr.set_x(pattr.get_x());
1589                           lwinAttr.set_y(pattr.get_y());
1590                           lwinAttr.set_border_width(lwinAttr.get_border_width()+pattr.get_border_width());
1591 
1592                           final long grand_parent = XlibUtil.getParentWindow(parent);
1593 
1594                           if (grand_parent == lwinAttr.get_root()) {
1595                               // This is not double-reparenting in a
1596                               // general sense - we simply don't have
1597                               // correct insets - return null to try to
1598                               // get insets later
1599                               return null;
1600                           } else {
1601                               parent = grand_parent;
1602                               XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1603                                                                parent,
1604                                                                pattr.pData);
1605                           }
1606                       }
1607                       /*
1608                        * XXX: To be absolutely correct, we'd need to take
1609                        * parent's border-width into account too, but the
1610                        * rest of the code is happily unaware about border
1611                        * widths and inner/outer distinction, so for the time
1612                        * being, just ignore it.
1613                        */
1614                       insLog.finest("Attrs before calculation: pattr({2})={0}, lwinAttr({3})={1}",
1615                                     lwinAttr, pattr, parent, window);
1616                       correctWM = new Insets(lwinAttr.get_y() + lwinAttr.get_border_width(),
1617                                              lwinAttr.get_x() + lwinAttr.get_border_width(),
1618                                              pattr.get_height() - (lwinAttr.get_y() + lwinAttr.get_height() + 2*lwinAttr.get_border_width()),
1619                                              pattr.get_width() -  (lwinAttr.get_x() + lwinAttr.get_width() + 2*lwinAttr.get_border_width()));
1620                       break;
1621                   } /* default */
1622                 } /* switch (runningWM) */
1623             } finally {
1624                 lwinAttr.dispose();
1625                 pattr.dispose();
1626             }
1627         }
1628         if (storedInsets.get(win.getClass()) == null) {
1629             storedInsets.put(win.getClass(), correctWM);
1630         }
1631         return correctWM;
1632     }
1633     boolean isDesktopWindow( long w ) {
1634         if (g_net_protocol != null) {
1635             XAtomList wtype = XAtom.get("_NET_WM_WINDOW_TYPE").getAtomListPropertyList( w );
1636             return wtype.contains( XAtom.get("_NET_WM_WINDOW_TYPE_DESKTOP") );
1637         } else {
1638             return false;
1639         }
1640     }
1641 
1642     public XNETProtocol getNETProtocol() {
1643         return g_net_protocol;
1644     }
1645 
1646     /**
1647      * Sets _NET_WN_ICON property on the window using the arrays of
1648      * raster-data for icons. If icons is null, removes _NET_WM_ICON
1649      * property.
1650      * This method invokes XNETProtocol.setWMIcon() for WMs that
1651      * support NET protocol.
1652      *
1653      * @return true if hint was modified successfully, false otherwise
1654      */
1655     public boolean setNetWMIcon(XWindowPeer window, java.util.List<XIconInfo> icons) {
1656         if (g_net_protocol != null && g_net_protocol.active()) {
1657             g_net_protocol.setWMIcons(window, icons);
1658             return getWMID() != ICE_WM;
1659         }
1660         return false;
1661     }
1662 }